﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Jinjia.Approve.Common.Enum;
using Jinjia.Approve.Entity.Entity.JJFlow;
using Jinjia.Approve.IRepository.JJFlow;
using Jinjia.Approve.IService.JJFlow;
using Jinjia.Approve.IService.JJFlow.BPM;
using Jinjia.Approve.Model.Input.JJFlow;
using Jinjia.Approve.Model.VM;
using Jinjia.Approve.Model.VM.JJFlow;
using Jinjia.Core.Attribute;
using JinJia.Core.Base.Exceptions;
using Microsoft.AspNetCore.Http;

namespace Jinjia.Approve.Service.JJFlow.BPM
{
    public class JJFlowBpmXMLService : JJFlowCommonService, IJJFlowBpmXMLService
    {
        private readonly IFlowDesignRepository _flowDesignRepository;
        private readonly IJJFlowBpmXMLParserService _jjFlowBpmXmlParserService;
        private readonly IFlowTemplateRepository _flowTemplateRepository;
        private readonly IFlowNodeRepository _flowNodeRepository;
        private readonly IFlowStepRepository _flowStepRepository;
        private readonly IFlowNodeEmpRepository _flowNodeEmpRepository;

        public JJFlowBpmXMLService(IHttpContextAccessor httpContextAccessor,
            IJJFlowClientService jjFlowClientService,
            IFlowDesignRepository flowDesignRepository,
            IJJFlowBpmXMLParserService jjFlowBpmXmlParserService,
            IFlowTemplateRepository flowTemplateRepository,
            IFlowNodeRepository flowNodeRepository,
            IFlowStepRepository flowStepRepository,
            IFlowNodeEmpRepository flowNodeEmpRepository) : base(httpContextAccessor,
            jjFlowClientService)
        {
            _flowDesignRepository = flowDesignRepository;
            _jjFlowBpmXmlParserService = jjFlowBpmXmlParserService;
            _flowTemplateRepository = flowTemplateRepository;
            _flowNodeRepository = flowNodeRepository;
            _flowStepRepository = flowStepRepository;
            _flowNodeEmpRepository = flowNodeEmpRepository;
        }


        public async Task<JJFlowDesignViewModel> GetDegineXMLDetail(int template_id)
        {
            var designXml = await _flowDesignRepository.GetByTemplateId(template_id);
            var jjFlowDesignViewModel = new JJFlowDesignViewModel()
            {
                template_id = template_id
            };
            if (designXml != null)
            {
                jjFlowDesignViewModel.id = designXml.id;
                jjFlowDesignViewModel.processXml = designXml.design_xml;
            }

            return jjFlowDesignViewModel;
        }

        [UseTran]
        public virtual async Task SaveBPMXML(JJFlowDesignEditModel model)
        {
            var bpmProcessModel = _jjFlowBpmXmlParserService.ParseXmlToModel(model.processXml);
            var template = await _flowTemplateRepository.GetById(model.template_id);
            template.update_user_id = GetUserId();
            template.update_time = DateTime.Now;
            await _flowTemplateRepository.Save(template);
            //保存更新Design
            var flowDesign = await _flowDesignRepository.GetByTemplateId(model.template_id) ?? new jj_flow_design
            {
                template_id = model.template_id,
                design_xml = model.processXml,
                deleted = 0,
                create_time = DateTime.Now,
                create_user_id = GetUserId()
            };

            flowDesign.design_xml = model.processXml;
            flowDesign.update_user_id = GetUserId();
            flowDesign.update_time = DateTime.Now;
            await _flowDesignRepository.Save(flowDesign);

            //数据库节点id和bpmxml里节点id映射
            var nodeIdMapping = new Dictionary<int, jj_node>();
            //数据库连线id和bpmxml里面节点id映射
            var stepIdMapping = new Dictionary<int, string>();

            //所有现存节点
            var allExitNodes = await _flowNodeRepository.GetNodes(model.template_id);
            //所有线程连线
            var allExitFlowStep = await _flowStepRepository.GetByTemplateId(model.template_id);

            #region 节点处理 节点新增  更新  删除

            #region 开始节点处理

            var startNode = await CalculateStartNode(template, allExitNodes, bpmProcessModel.StartEvent);
            nodeIdMapping.Add(startNode.id, startNode);

            #endregion

            #region 用户任务节点

            foreach (var item in bpmProcessModel.UserTasks)
            {
                var userTaskNode = await CalculateUserTaskNode(template, allExitNodes, item);
                nodeIdMapping.Add(userTaskNode.id, userTaskNode);
            }

            #endregion

            #region 条件节点

            foreach (var item in bpmProcessModel.ExclusiveGateways)
            {
                var gatewayNode = await CalculateExclusiveGatewayNode(template, allExitNodes, item);
                nodeIdMapping.Add(gatewayNode.id, gatewayNode);
            }

            #endregion

            #region 结束节点

            foreach (var item in bpmProcessModel.EndEvents)
            {
                var endNode = await CalculateEndEventsNode(template, allExitNodes, item);
                nodeIdMapping.Add(endNode.id, endNode);
            }

            #endregion

            #region 流程中 删除掉的节点

            var deleteExitNodes = allExitNodes.Where(t => !nodeIdMapping.Keys.Contains(t.id)).ToList();
            foreach (var item in deleteExitNodes)
            {
                item.deleted = 1;
                item.update_user_id = GetUserId();
                await _flowNodeRepository.Save(item);
            }

            #endregion

            #endregion

            #region 处理节点等级 下一节点   。只有节点保存完后才能拿到节点id。所以这个过程必须在节点报存完毕后操作

            foreach (var item in nodeIdMapping)
            {
                if (bpmProcessModel.NodeList.ContainsKey(item.Value.bpm_nodeid))
                {
                    item.Value.node_step = bpmProcessModel.NodeList[item.Value.bpm_nodeid];

                    if (bpmProcessModel.TargetList.ContainsKey(item.Value.bpm_nodeid))
                    {
                        var toNodeIdList = nodeIdMapping.Values.Where(t => bpmProcessModel.TargetList[item.Value.bpm_nodeid].Contains(t.bpm_nodeid)).Select(t => t.id).ToList();
                        item.Value.to_node = string.Join(",", toNodeIdList);
                    }

                    await _flowNodeRepository.Save(item.Value);
                }
            }

            #endregion

            #region 节点连线处理   新增  更新   删除

            foreach (var item in bpmProcessModel.SequenceFlows)
            {
                var flowSteps = await CalculateFlowStep(template, allExitFlowStep, nodeIdMapping, item, bpmProcessModel.TargetList);
                foreach (var flowStep in flowSteps)
                {
                    if (!stepIdMapping.ContainsKey(flowStep.id))
                    {
                        stepIdMapping.Add(flowStep.id, flowStep.bpm_nodeid);
                    }
                }
            }

            //计算结束节点
            var endFlowSteps = await CalculateEndFlowStep(template, allExitFlowStep, nodeIdMapping, bpmProcessModel.TargetList);
            foreach (var item in endFlowSteps)
            {
                stepIdMapping.Add(item.id, "");
            }

            //删除无效连线
            var deleteStep = allExitFlowStep.Where(t => !stepIdMapping.Keys.Contains(t.id)).ToList();
            foreach (var item in deleteStep)
            {
                item.deleted = 1;
                item.update_user_id = GetUserId();
                await _flowStepRepository.Save(item);
            }

            #endregion
        }


        /// <summary>
        /// 计算开始节点
        /// </summary>
        /// <param name="template"></param>
        /// <param name="allExitNodes"></param>
        /// <param name="startEvent"></param>
        private async Task<jj_node> CalculateStartNode(jj_flow_template template, List<jj_node> allExitNodes, JJFlowBPMStartEventVM startEvent)
        {
            var startNode = new jj_node();
            if (allExitNodes.Any(t => t.bpm_nodeid == startEvent.id))
            {
                startNode = allExitNodes.First(t => t.bpm_nodeid == startEvent.id);
                startNode.update_user_id = GetUserId();
            }
            else
            {
                startNode.node_type = 0;
                startNode.template_id = template.id;
                startNode.template_name = template.template_name;
                startNode.to_node = string.Empty;
                startNode.bpm_nodeid = startEvent.id;
                startNode.create_time = startNode.update_time = DateTime.Now;
                startNode.create_user_id = startNode.update_user_id = GetUserId();
            }

            startNode.node_name = startEvent.name;
            startNode.node_type = (int)EnumDb.JJFlowNodeTypeEnum.StartNode;
            startNode.remark = startEvent.remark;

            var id = await _flowNodeRepository.Save(startNode);
            startNode.id = id;
            return startNode;
        }

        /// <summary>
        /// 计算用户任务节点
        /// </summary>
        /// <param name="template"></param>
        /// <param name="allExitNodes"></param>
        /// <param name="userTaskBPM"></param>
        private async Task<jj_node> CalculateUserTaskNode(jj_flow_template template, List<jj_node> allExitNodes, JJFlowBPMUserTaskVM userTaskBPM)
        {
            var userTaskNode = new jj_node();
            if (allExitNodes.Any(t => t.bpm_nodeid == userTaskBPM.id))
            {
                userTaskNode = allExitNodes.First(t => t.bpm_nodeid == userTaskBPM.id);
                userTaskNode.update_user_id = GetUserId();
            }
            else
            {
                userTaskNode.node_type = 0;
                userTaskNode.template_id = template.id;
                userTaskNode.template_name = template.template_name;
                userTaskNode.to_node = string.Empty;
                userTaskNode.bpm_nodeid = userTaskBPM.id;
                userTaskNode.create_time = userTaskNode.update_time = DateTime.Now;
                userTaskNode.create_user_id = userTaskNode.update_user_id = GetUserId();
            }

            userTaskNode.node_name = userTaskBPM.name;
            userTaskNode.node_type = (int)EnumDb.JJFlowNodeTypeEnum.UserTaskNode;
            userTaskNode.remark = userTaskBPM.remark;
            userTaskNode.power_type = userTaskBPM.power_type;

            //节点规则
            userTaskNode.auto_approve = userTaskBPM.auto_approve;
            userTaskNode.approve_rule = userTaskBPM.approve_rule;
            userTaskNode.reject_rule = userTaskBPM.reject_rule;

            //完成策略
            userTaskNode.emp_type = userTaskBPM.signType;

            #region 节点按钮权限

            userTaskNode.is_return = userTaskBPM.action.Contains((int)EnumDb.JJFlowNodeActionTypeEnum.Back) ? 1 : 0;

            #endregion

            if (userTaskNode.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.Function || userTaskNode.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.RemoteFunction)
            {
                userTaskNode.class_name = userTaskBPM.assigneeField;
            }
            else if (userTaskNode.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.User)
            {
                #region 存对应的jj_flow_node_emp

                #endregion
            }
            else if (userTaskNode.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.Role)
            {
                #region 存对应的jj_flow_node_emp

                #endregion
            }
            else if (userTaskNode.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.Organizational)
            {
                int.TryParse(userTaskBPM.orgLevel, out var orgLevel);
                userTaskNode.power_org_level = orgLevel;
            }

            var id = await _flowNodeRepository.Save(userTaskNode);
            userTaskNode.id = id;

            if (userTaskNode.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.User || userTaskNode.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.Role)
            {
                await CalculateUserTaskNodeNodeEmp(userTaskNode, userTaskBPM);
            }

            return userTaskNode;
        }

        #region 节点人员处理

        /// <summary>
        /// 计算指定用户
        /// </summary>
        /// <param name="node"></param>
        /// <param name="userTask"></param>
        private async Task CalculateUserTaskNodeNodeEmp(jj_node node, JJFlowBPMUserTaskVM userTask)
        {
            var nodeEmpList = await _flowNodeEmpRepository.GetByNodeId(node.id, node.template_id);

            //清空 对应的jj_flow_node_emp
            await _flowNodeEmpRepository.PhysicalDelete(nodeEmpList.Select(x => x.id).ToList());
            if (node.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.User)
            {
                var jjFlowNodeEmps = new List<jj_flow_node_emp>();
                foreach (var item in userTask.userIdList)
                {
                    jjFlowNodeEmps.Add(new jj_flow_node_emp
                    {
                        node_id = node.id,
                        user_id = item,
                        template_id = node.template_id,
                    });
                }

                await _flowNodeEmpRepository.Save(jjFlowNodeEmps);
            }
            else if (node.power_type == (int)EnumDb.JJFlowNodeRoleTypeEnum.Role)
            {
                var jjFlowNodeEmps = new List<jj_flow_node_emp>();
                foreach (var item in userTask.roleIdList)
                {
                    jjFlowNodeEmps.Add(new jj_flow_node_emp
                    {
                        node_id = node.id,
                        user_id = 0,
                        role_code = item,
                        template_id = node.template_id,
                    });
                }

                await _flowNodeEmpRepository.Save(jjFlowNodeEmps);
            }
        }

        #endregion

        /// <summary>
        /// 计算条件节点
        /// </summary>
        /// <param name="template"></param>
        /// <param name="allExitNodes"></param>
        /// <param name="gatewayBPM"></param>
        private async Task<jj_node> CalculateExclusiveGatewayNode(jj_flow_template template, List<jj_node> allExitNodes, JJFlowBPMExclusiveGatewayVM gatewayBPM)
        {
            var gateWaysNode = new jj_node();
            if (allExitNodes.Any(t => t.bpm_nodeid == gatewayBPM.id))
            {
                gateWaysNode = allExitNodes.First(t => t.bpm_nodeid == gatewayBPM.id);
                gateWaysNode.update_user_id = GetUserId();
            }
            else
            {
                gateWaysNode.node_type = 0;
                gateWaysNode.template_id = template.id;
                gateWaysNode.template_name = template.template_name;
                gateWaysNode.to_node = string.Empty;
                gateWaysNode.bpm_nodeid = gatewayBPM.id;
                gateWaysNode.create_time = gateWaysNode.update_time = DateTime.Now;
                gateWaysNode.create_user_id = gateWaysNode.update_user_id = GetUserId();
            }

            gateWaysNode.node_name = gatewayBPM.name;
            gateWaysNode.node_type = (int)EnumDb.JJFlowNodeTypeEnum.CondNode;
            gateWaysNode.remark = gatewayBPM.remark;
            gateWaysNode.power_type = 0;
            gateWaysNode.emp_type = (int)EnumDb.JJFlowNodeEmpTypeEnum.OrNode;
            gateWaysNode.class_name = string.Empty;

            var id = await _flowNodeRepository.Save(gateWaysNode);
            gateWaysNode.id = id;
            return gateWaysNode;
        }

        /// <summary>
        /// 计算结束节点
        /// </summary>
        /// <param name="template"></param>
        /// <param name="allExitNodes"></param>
        /// <param name="endEventBPM"></param>
        private async Task<jj_node> CalculateEndEventsNode(jj_flow_template template, List<jj_node> allExitNodes, JJFlowBPMEndEventVM endEventBPM)
        {
            var endNode = new jj_node();
            if (allExitNodes.Any(t => t.bpm_nodeid == endEventBPM.id))
            {
                endNode = allExitNodes.First(t => t.bpm_nodeid == endEventBPM.id);
                endNode.update_user_id = GetUserId();
            }
            else
            {
                endNode.node_type = 0;
                endNode.template_id = template.id;
                endNode.template_name = template.template_name;
                endNode.to_node = string.Empty;
                endNode.bpm_nodeid = endEventBPM.id;
                endNode.create_time = endNode.update_time = DateTime.Now;
                endNode.create_user_id = endNode.update_user_id = GetUserId();
            }

            endNode.node_name = endEventBPM.name;
            endNode.node_type = (int)EnumDb.JJFlowNodeTypeEnum.EndNode;
            endNode.remark = endEventBPM.remark;
            endNode.power_type = 0;
            endNode.emp_type = (int)EnumDb.JJFlowNodeEmpTypeEnum.OrNode;
            endNode.class_name = string.Empty;

            var id = await _flowNodeRepository.Save(endNode);
            endNode.id = id;
            return endNode;
        }

        #region 节点连线计算保存

        private async Task<List<jj_flow_step>> CalculateFlowStep(jj_flow_template template, List<jj_flow_step> allExitStep, Dictionary<int, jj_node> nodeIdMapping,
            JJFlowBPMSequenceFlowVM sequenceFlowBPM, Dictionary<string, List<string>> targetList)
        {
            var stepNodes = new List<jj_flow_step>();
            var fromIds = new List<int>() { 0 };
            var fromNodeKeys = targetList.Where(t => t.Value.Contains(sequenceFlowBPM.sourceRef)).Select(t => t.Key).ToList();
            if (nodeIdMapping.Values.Any(t => fromNodeKeys.Contains(t.bpm_nodeid)))
            {
                fromIds = nodeIdMapping.Values.Where(t => fromNodeKeys.Contains(t.bpm_nodeid)).Select(t => t.id).ToList();
            }

            foreach (var fromId in fromIds)
            {
                var stepNode = new jj_flow_step();
                if (allExitStep.Any(t => t.bpm_nodeid == sequenceFlowBPM.id && t.from_node == fromId))
                {
                    stepNode = allExitStep.First(t => t.bpm_nodeid == sequenceFlowBPM.id && t.from_node == fromId);
                    stepNode.update_user_id = GetUserId();
                }
                else
                {
                    stepNode.template_id = template.id;
                    stepNode.bpm_nodeid = sequenceFlowBPM.id;
                    stepNode.create_time = stepNode.update_time = DateTime.Now;
                    stepNode.create_user_id = stepNode.update_user_id = GetUserId();
                }

                stepNode.from_node = fromId;

                if (nodeIdMapping.Any(t => t.Value.bpm_nodeid == sequenceFlowBPM.targetRef))
                {
                    stepNode.to_node = nodeIdMapping.First(t => t.Value.bpm_nodeid == sequenceFlowBPM.targetRef).Key;
                }

                if (nodeIdMapping.Any(t => t.Value.bpm_nodeid == sequenceFlowBPM.sourceRef))
                {
                    stepNode.current_node = nodeIdMapping.First(t => t.Value.bpm_nodeid == sequenceFlowBPM.sourceRef).Key;
                }

                stepNode.current_user = "-1";

                #region 条件

                int.TryParse(sequenceFlowBPM.condition_type, out var conditionType);
                stepNode.condition_type = conditionType;
                stepNode.condition_name = sequenceFlowBPM.condition_name;
                if (conditionType == (int)EnumDb.JJFlowSequenceConditionTypeEnum.Expression)
                {
                    //校验是否存在条件表达式 运算符号
                    if (!CheckConditionOperator(sequenceFlowBPM.condition_name))
                    {
                        throw new BusinessException("条件表达式异常！请确认是否填写规范正确~");
                    }
                }
                else if (conditionType == (int)EnumDb.JJFlowSequenceConditionTypeEnum.Function || conditionType == (int)EnumDb.JJFlowSequenceConditionTypeEnum.RemoteFunction)
                {
                    if (sequenceFlowBPM.condition_name.StartsWith("!"))
                    {
                        stepNode.is_condition_satified = false;
                        stepNode.condition_name = sequenceFlowBPM.condition_name.TrimStart('!');
                    }
                    else
                    {
                        stepNode.is_condition_satified = true;
                        stepNode.condition_name = sequenceFlowBPM.condition_name;
                    }
                }

                #endregion

                stepNode.priority = sequenceFlowBPM.priority;
                stepNode.remark = sequenceFlowBPM.remark;
                var id = await _flowStepRepository.Save(stepNode);
                stepNode.id = id;
                stepNodes.Add(stepNode);
            }

            return stepNodes;
        }

        private async Task<List<jj_flow_step>> CalculateEndFlowStep(jj_flow_template template, List<jj_flow_step> allExitStep, Dictionary<int, jj_node> nodeIdMapping,
            Dictionary<string, List<string>> targetList)
        {
            var stepNodes = new List<jj_flow_step>();
            //所有结束节点
            var endNodes = nodeIdMapping.Values.Where(t => t.node_type == (int)EnumDb.JJFlowNodeTypeEnum.EndNode).ToList();
            foreach (var item in endNodes)
            {
                //结束节点对应开始节点
                var allFormNode = targetList.Where(t => t.Value.Contains(item.bpm_nodeid)).Select(t => t.Key).ToList();
                foreach (var fromNode in allFormNode)
                {
                    var fromNodeId = nodeIdMapping.First(t => t.Value.bpm_nodeid == fromNode).Value.id;
                    if (allExitStep.Any(t => t.from_node == fromNodeId && t.to_node == 0 && t.current_node == item.id))
                    {
                        var stepNode = allExitStep.First(t => t.from_node == fromNodeId && t.to_node == 0 && t.current_node == item.id);
                        stepNode.from_node = fromNodeId;
                        stepNode.to_node = 0;
                        stepNode.current_node = item.id;
                        stepNode.update_time = DateTime.Now;
                        stepNode.update_user_id = GetUserId();
                        stepNodes.Add(stepNode);
                    }
                    else
                    {
                        var stepNode = new jj_flow_step
                        {
                            template_id = template.id,
                            from_node = fromNodeId,
                            to_node = 0,
                            current_node = item.id,
                            create_time = DateTime.Now,
                            update_time = DateTime.Now,
                            create_user_id = GetUserId(),
                            update_user_id = GetUserId()
                        };
                        var id = await _flowStepRepository.Save(stepNode);
                        stepNode.id = id;
                        stepNodes.Add(stepNode);
                    }
                }
            }

            return stepNodes;
        }

        private bool CheckConditionOperator(string condition_name)
        {
            return true;
        }

        #endregion
    }
}